﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Csla;
using BizControl.DalLinq;
using System.ComponentModel.DataAnnotations;
using Csla.Data;
using Csla.Rules.CommonRules;
using Csla.Rules;
using System.Data.Linq;
using BizControl.Library.BusinessObjects.Comments;
using BizControl.Library.BusinessObjects.People;
using BizControl.Library.BusinessObjects.Participants;
using BizControl.Library.BusinessObjects.Models;
using BizControl.Library.Criterias;
using BizControl.Library.BusinessObjects.Categories;
using BizControl.Resources;
using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;
using Microsoft.Practices.EnterpriseLibrary.Validation;
using BizControl.Library.Security;
using BizControl.Library.BusinessObjects.Marks;

namespace BizControl.Library.BusinessObjects.Activities
{
  [Serializable()]
  public class ActivityBO : BusinessBase<ActivityBO>, ISupportEmptyValue
  {
    #region Business Methods

    private static readonly PropertyInfo<Guid> ActivityIdProperty = RegisterProperty<Guid>(c => c.ActivityId);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"ActivityIdRequiredError")]
    public Guid ActivityId
    {
      get { return GetProperty(ActivityIdProperty); }
      private set { SetProperty(ActivityIdProperty, value); }
    }

    private static readonly PropertyInfo<string> TagsProperty = RegisterProperty<string>(c => c.Tags);
    [Display(ResourceType = typeof(PropertiesNames), Name = @"ActivityTagsProperty")]
    public string[] Tags
    {
      get { return GetProperty(TagsProperty).Split(','); }
      set
      {
        StringBuilder tagsSB = new StringBuilder();
        for (int i = 0; i < value.Length; i++)
        {
          tagsSB.Append(value[i]);
          if (i > 0 && i + 1 < value.Length) tagsSB.Append(",");
        }
        SetProperty(TagsProperty, tagsSB.ToString());
      }
    }

    private static readonly PropertyInfo<string> DescriptionProperty = RegisterProperty<string>(c => c.Description);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"ActivityDescriptionRequiredError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"ActivityDescriptionProperty")]
    public string Description
    {
      get { return GetProperty(DescriptionProperty); }
      set { SetProperty(DescriptionProperty, value); OnPropertyChanged(ShortenDescriptionProperty); }
    }

    private static readonly PropertyInfo<string> ShortenDescriptionProperty = RegisterProperty<string>(c => c.ShortenDescription);
    [Display(ResourceType = typeof(PropertiesNames), Name = @"ActivityShortenDescriptionProperty")]
    public string ShortenDescription
    {
      get
      {
        return this.GetShortenDescription();
      }
    }

    private static readonly PropertyInfo<DateTime?> StartDateProperty = RegisterProperty<DateTime?>(c => c.StartDate);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"ActivityStartDateRequiredError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"ActivityStartDateProperty")]
    public DateTime? StartDate
    {
      get { return GetProperty(StartDateProperty); }
      set
      {
        if (value == DateTime.MinValue)
          SetProperty(StartDateProperty, null);
        else
          SetProperty(StartDateProperty, value);
      }
    }

    private static readonly PropertyInfo<DateTime?> DueDateProperty = RegisterProperty<DateTime?>(c => c.DueDate);
    [Display(ResourceType = typeof(PropertiesNames), Name = @"ActivityDueDateProperty")]
    public DateTime? DueDate
    {
      get { return GetProperty(DueDateProperty); }
      set { SetProperty(DueDateProperty, value); }
    }

    private static readonly PropertyInfo<DateTime?> EndedProperty = RegisterProperty<DateTime?>(c => c.Ended);
    [Display(ResourceType = typeof(PropertiesNames), Name = @"ActivityEndedProperty")]
    public DateTime? Ended
    {
      get { return GetProperty(EndedProperty); }
      set { SetProperty(EndedProperty, value); }
    }

    private static readonly PropertyInfo<int> ProgressProperty = RegisterProperty<int>(c => c.Progress, null, (int)ProgressState.Zero);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"ActivityProgressRequiredError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"ActivityProgressProperty")]
    public int Progress
    {
      get
      {
        LoadProperty(ProgressProperty, CalculateProgress());
        BusinessRules.CheckRules(ProgressProperty);
        return GetProperty(ProgressProperty);
      }
    }

    private static readonly PropertyInfo<bool> IsCompletedProperty = RegisterProperty<bool>(c => c.IsCompleted, null, false);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"ActivityIsCompletedRequiredError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"ActivityIsCompletedProperty")]
    public bool IsCompleted
    {
      get { return GetProperty(IsCompletedProperty); }
      set { SetProperty(IsCompletedProperty, value); }
    }

    private static readonly PropertyInfo<bool> IsHierarchicalProperty = RegisterProperty<bool>(c => c.IsHierarchical, null, false);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"ActivityIsHierarchicalRequiredError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"ActivityIsHierarchicalProperty")]
    public bool IsHierarchical
    {
      get { return GetProperty(IsHierarchicalProperty); }
      set { SetProperty(IsHierarchicalProperty, value); }
    }

    private static readonly PropertyInfo<bool> IsTemplateProperty = RegisterProperty<bool>(c => c.IsTemplate, null, false);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"ActivityIsTemplateRequiredError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"ActivityIsTemplateProperty")]
    public bool IsTemplate
    {
      get { return GetProperty(IsTemplateProperty); }
      set { SetProperty(IsTemplateProperty, value); }
    }

    private static readonly PropertyInfo<Guid?> ModelProperty = RegisterProperty<Guid?>(c => c.ModelId);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"ModelRequiredError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"ModelIdProperty")]
    internal Guid? ModelId
    {
      get { return GetProperty(ModelProperty); }
      set { SetProperty(ModelProperty, value); }
    }

    private static readonly PropertyInfo<ObjectPriority> PriorityProperty =
      RegisterProperty<ObjectPriority>(c => c.Priority, null, ObjectPriority.Normal);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"ActivityPriorityRequiredError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"ActivityPriorityProperty")]
    public int Priority
    {
      get { return GetPropertyConvert<ObjectPriority, int>(PriorityProperty); }
      set { SetPropertyConvert<ObjectPriority, int>(PriorityProperty, value); }
    }

    private static readonly PropertyInfo<Guid?> CategoryProperty = RegisterProperty<Guid?>(c => c.CategoryId);
    [Display(ResourceType = typeof(PropertiesNames), Name = @"ActivityCategoryProperty")]
    public Guid? CategoryId
    {
      get { return GetProperty(CategoryProperty); }
      set { SetProperty(CategoryProperty, value); }
    }

    private static readonly PropertyInfo<int?> QualityProperty = RegisterProperty<int?>(c => c.Quality);
    [Display(ResourceType = typeof(PropertiesNames), Name = @"ActivityQualityProperty")]
    public int? Quality
    {
      get { return GetProperty(QualityProperty); }
      set { SetProperty(QualityProperty, value); }
    }

    private static readonly PropertyInfo<bool> HasMarksProperty = RegisterProperty<bool>(c => c.HasMarks);
    public bool HasMarks
    {
      get { return Marks.Count > 0; }
      set
      {
        OnPropertyChanged(HasMarksProperty);
      }
    }

    private static readonly PropertyInfo<PersonInfo> ResponsiblePersonProperty = RegisterProperty<PersonInfo>(c => c.ResponsiblePerson);
    [Display(ResourceType = typeof(PropertiesNames), Name = @"ActivityResponsiblePersonProperty")]
    public PersonInfo ResponsiblePerson
    {
      get { return GetProperty(ResponsiblePersonProperty); }
      set { SetProperty(ResponsiblePersonProperty, value); }
    }

    private static readonly PropertyInfo<bool> CanChangeResponsiblePersonProperty = RegisterProperty<bool>(c => c.CanChangeResponsiblePerson);
    public bool CanChangeResponsiblePerson
    {
      get
      {
        if (CreatedBy.Id == SecurityChecks.GetCurrentUser().PersonId || (ResponsiblePerson == null || ResponsiblePerson.IsEmpty()))
          return true;
        return false;
      }
      set
      {
        OnPropertyChanged(CanChangeResponsiblePersonProperty);
      }
    }

    private static readonly PropertyInfo<bool> CanSetCompleteProperty = RegisterProperty<bool>(c => c.CanSetComplete);
    public bool CanSetComplete
    {
      get
      {
        if (CreatedBy.Id == SecurityChecks.GetCurrentUser().PersonId)
          return true;
        foreach (var mark in Marks)
        {
          if (!mark.IsActive) return false;
        }
        foreach (var activity in DependentActivities)
        {
          if ((ActivityLinkType)Enum.Parse(typeof(ActivityLinkType), activity.Condition) == ActivityLinkType.Child)
            if (!activity.Activity2.State.HasFlag(ActivityState.Completed))
              return false;
        }
        if (PreviousActivity != null && !PreviousActivity.IsEmpty() && !PreviousActivity.State.HasFlag(ActivityState.Completed))
          return false;
        if (ResponsiblePerson != null && !ResponsiblePerson.IsEmpty() && ResponsiblePerson.Id == SecurityChecks.GetCurrentUser().PersonId)
          return true;
        return false;
      }
      set
      {
        OnPropertyChanged(CanSetCompleteProperty);
      }
    }

    private static readonly PropertyInfo<ActivityState> StateProperty = RegisterProperty<ActivityState>(c => c.State);
    public ActivityState State
    {
      get
      {
        ActivityState state;
        if (IsCompleted) state = ActivityState.Completed;
        else state = ActivityState.Normal;
        if ((!IsCompleted && DueDate < DateTime.Now)
          || (IsCompleted && DueDate < Ended)) state = state | ActivityState.Overdue;
        if ((!IsCompleted && StartDate > DateTime.Now) 
          || (PreviousActivity != null && !PreviousActivity.IsEmpty() && !PreviousActivity.State.HasFlag(ActivityState.Completed))
          || (ParentActivity != null && ParentActivity.State.HasFlag(ActivityState.NotStarted)))
          state = state | ActivityState.NotStarted;
        // Also NotStarted depends on conditions
        return state;
      }
    }

    private static readonly PropertyInfo<ActivityInfo> PreviousActivityProperty = RegisterProperty<ActivityInfo>(c => c.PreviousActivity);
    public ActivityInfo PreviousActivity
    {
      get { return GetProperty(PreviousActivityProperty); }
      private set { SetProperty(PreviousActivityProperty, value); OnPropertyChanged(HasPreviousActivityProperty); }
    }

    private static readonly PropertyInfo<bool> HasPreviousActivityProperty = RegisterProperty<bool>(c => c.HasPreviousActivity);
    public bool HasPreviousActivity
    {
      get { return PreviousActivity != null && !PreviousActivity.IsEmpty(); }
    }

    private static readonly PropertyInfo<ActivityInfo> ParentActivityProperty = RegisterProperty<ActivityInfo>(c => c.ParentActivity);
    public ActivityInfo ParentActivity
    {
      get { return GetProperty(ParentActivityProperty); }
      private set { SetProperty(ParentActivityProperty, value); OnPropertyChanged(HasParentActivityProperty); }
    }

    private static readonly PropertyInfo<bool> HasParentActivityProperty = RegisterProperty<bool>(c => c.HasParentActivity);
    public bool HasParentActivity
    {
      get { return ParentActivity != null && !ParentActivity.IsEmpty(); }
    }

    #region Common properties

    private static readonly PropertyInfo<bool> IsDeletedFieldProperty = RegisterProperty<bool>(c => c.IsDeletedField, null, false);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"IsDeletedRequiredError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"IsDeletedFieldProperty")]
    public bool IsDeletedField
    {
      get { return GetProperty(IsDeletedFieldProperty); }
      private set { SetProperty(IsDeletedFieldProperty, value); }
    }

    private static readonly PropertyInfo<PersonInfo> CreatedByProperty = RegisterProperty<PersonInfo>(c => c.CreatedBy);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"CreatedByRequiredPropertyError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"CreatedByProperty")]
    public PersonInfo CreatedBy
    {
      get { return GetProperty(CreatedByProperty); }
      private set { SetProperty(CreatedByProperty, value); }
    }

    private static readonly PropertyInfo<DateTime> CreatedOnProperty = RegisterProperty<DateTime>(c => c.CreatedOn);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"CreatedOnRequiredPropertyError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"CreatedOnProperty")]
    public DateTime CreatedOn
    {
      get { return GetProperty(CreatedOnProperty); }
      private set { SetProperty(CreatedOnProperty, value); }
    }

    private static readonly PropertyInfo<DateTime> LastChangedProperty = RegisterProperty<DateTime>(c => c.LastChanged);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"LastChangedRequiredPropertyError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"LastChangedProperty")]
    public DateTime LastChanged
    {
      get { return GetProperty(LastChangedProperty); }
      private set { SetProperty(LastChangedProperty, value); }
    }

    private static readonly PropertyInfo<PersonInfo> LastChangedByProperty = RegisterProperty<PersonInfo>(c => c.LastChangedBy);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"LastChangedByRequiredPropertyError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"LastChangedByProperty")]
    public PersonInfo LastChangedBy
    {
      get { return GetProperty(LastChangedByProperty); }
      private set { SetProperty(LastChangedByProperty, value); }
    }

    private static readonly PropertyInfo<byte[]> TimeStampProperty = RegisterProperty<byte[]>(c => c.TimeStamp);
    private byte[] TimeStamp
    {
      get { return GetProperty(TimeStampProperty); }
      set { SetProperty(TimeStampProperty, value); }
    }

    #endregion

    #region Children

    private static readonly PropertyInfo<ActivityComments> CommentsProperty = 
      RegisterProperty<ActivityComments>(p => p.Comments, RelationshipTypes.Child | RelationshipTypes.LazyLoad);
    public ActivityComments Comments
    {
      get
      {
        if (!(FieldManager.FieldExists(CommentsProperty)))
          Comments = ActivityComments.NewActivityComments(this);
        return GetProperty(CommentsProperty);
      }
      private set
      {
        LoadProperty(CommentsProperty, value);
        OnPropertyChanged(CommentsProperty);
      }
    }

    private static readonly PropertyInfo<ActivityParticipants> ParticipantsProperty = 
      RegisterProperty<ActivityParticipants>(p => p.Participants, RelationshipTypes.Child | RelationshipTypes.LazyLoad);
    public ActivityParticipants Participants
    {
      get
      {
        if (!(FieldManager.FieldExists(ParticipantsProperty)))
          Participants = ActivityParticipants.NewActivityParticipants(this);
        return GetProperty(ParticipantsProperty);
      }
      private set
      {
        LoadProperty(ParticipantsProperty, value);
        OnPropertyChanged(ParticipantsProperty);
      }
    }

    private static readonly PropertyInfo<ActivityMarks> MarksProperty = 
      RegisterProperty<ActivityMarks>(p => p.Marks, RelationshipTypes.Child | RelationshipTypes.LazyLoad);
    public ActivityMarks Marks
    {
      get
      {
        if (!(FieldManager.FieldExists(MarksProperty)))
          Marks = ActivityMarks.NewActivityMarks(this);
        return GetProperty(MarksProperty);
      }
      private set
      {
        LoadProperty(MarksProperty, value);
        OnPropertyChanged(MarksProperty);
      }
    }

    private static readonly PropertyInfo<ActivityLinks> DependentActivitiesProperty = 
      RegisterProperty<ActivityLinks>(p => p.DependentActivities, RelationshipTypes.Child | RelationshipTypes.LazyLoad);
    public ActivityLinks DependentActivities
    {
      get
      {
        if (!(FieldManager.FieldExists(DependentActivitiesProperty)))
          DependentActivities = ActivityLinks.NewActivityLinks(this);
        return GetProperty(DependentActivitiesProperty);
      }
      private set
      {
        LoadProperty(DependentActivitiesProperty, value);
        OnPropertyChanged(DependentActivitiesProperty);
      }
    }

    public ActivityList ChildActivities
    {
      get
      {
        var activities = ReadProperty(DependentActivitiesProperty)
          .Where(a => (ActivityLinkType)Enum.Parse(typeof(ActivityLinkType), a.Condition) == ActivityLinkType.Child);
        ActivityList childActivities = ActivityList.NewActivityList();
        childActivities.RaiseListChangedEvents = false;
        foreach (var activity in activities)
        {
          childActivities.Add(new ActivityInfo(activity.Activity2));
        }
        childActivities.RaiseListChangedEvents = true;
        return childActivities;
      }
    }

    public ActivityList NextActivities
    {
      get
      {
        var activities = ReadProperty(DependentActivitiesProperty)
          .Where(a => (ActivityLinkType)Enum.Parse(typeof(ActivityLinkType), a.Condition) == ActivityLinkType.Next);
        ActivityList nextActivities = ActivityList.NewActivityList();
        nextActivities.RaiseListChangedEvents = false;
        foreach (var activity in activities)
        {
          nextActivities.Add(new ActivityInfo(activity.Activity2));
        }
        nextActivities.RaiseListChangedEvents = true;
        return nextActivities;
      }
    }

    #endregion

    #region Methods

    private static readonly MethodInfo SetCompleteMethod = RegisterMethod(typeof(ActivityBO), "SetComplete");
    public void SetComplete()
    {
      //CanExecuteMethod(SetCompleteMethod);
      if (IsCompleted)
        IsCompleted = false;
      else
        IsCompleted = true;
    }

    private static readonly MethodInfo GetIndependentActivitiesMethod = RegisterMethod(typeof(ActivityBO), "GetIndependentActivities");
    public IndependentActivityListForActivity GetIndependentActivities()
    {
      //CanExecuteMethod(GetIndependentActivities);
      return IndependentActivityListForActivity.GetIndependentActivityListForActivity(this);
    }

    #endregion

    internal void LoadData(Activity activityDB)
    {
      if (activityDB == null) return;
      ActivityId = activityDB.ActivityId;
      LoadProperty(TagsProperty, activityDB.Tags);
      Description = activityDB.Description;
      Priority = activityDB.Priority;
      CategoryId = activityDB.Category;
      IsCompleted = activityDB.IsCompleted;
      StartDate = activityDB.StartDate;
      DueDate = activityDB.DueDate;
      Ended = activityDB.Ended;
      LoadProperty(ProgressProperty, activityDB.Progress);
      Quality = activityDB.Quality;
      ModelId = activityDB.Model;
      ResponsiblePerson = new PersonInfo(activityDB.ResponsiblePersonRef);
      ParentActivity = new ActivityInfo((from a in activityDB.ActivityLinks1
                                         where a.Activity2 == ActivityId 
                                         && (ActivityLinkType)Enum.Parse(typeof(ActivityLinkType), a.Condition) == ActivityLinkType.Child
                                         select a.Activity1Ref).FirstOrDefault());
      PreviousActivity = new ActivityInfo((from a in activityDB.ActivityLinks1
                                           where a.Activity2 == ActivityId 
                                           && (ActivityLinkType)Enum.Parse(typeof(ActivityLinkType), a.Condition) == ActivityLinkType.Next
                                           select a.Activity1Ref).FirstOrDefault());

      // Common properties
      CreatedBy = new PersonInfo(activityDB.CreatedByRef);
      LastChangedBy = new PersonInfo(activityDB.LastChangedByRef);

      CreatedOn = activityDB.CreatedOn;
      LastChanged = activityDB.LastChanged;

      IsDeletedField = activityDB.IsDeleted;
      TimeStamp = activityDB.TimeStamp.ToArray();
    }

    public override string ToString()
    {
      return ActivityId.ToString();
    }

    public void UpdateChildActivities()
    {
      OnPropertyChanged("ChildActivities");
    }

    public void UpdateNextActivities()
    {
      OnPropertyChanged("NextActivities");
    }

    public void UpdateProgress()
    {
      OnPropertyChanged(ProgressProperty);
    }

    internal void UpdateCanComplete()
    {
      OnPropertyChanged(CanSetCompleteProperty);
    }

    private int CalculateProgress()
    {
      if (IsCompleted) return (int)ProgressState.Completed;

      if (Marks.Count == 0) return (int)ProgressState.Zero;

      int totalWeight = 0;
      int completedWeight = 0;
      foreach (var mark in Marks)
      {
        totalWeight += mark.Weight;
        if (mark.IsActive)
          completedWeight += mark.Weight;
      }
      if (totalWeight == 0) return (int)ProgressState.Zero;
      return (int)((double)completedWeight / (double)totalWeight * (int)ProgressState.Completed);
    }

    #endregion

    #region Business Rules

    protected override void AddBusinessRules()
    {
      base.AddBusinessRules();
      BusinessRules.AddRule(new StartDateGTEndDate { PrimaryProperty = StartDateProperty, AffectedProperties = { EndedProperty, DueDateProperty } });
      BusinessRules.AddRule(new StartDateGTEndDate { PrimaryProperty = EndedProperty, AffectedProperties = { StartDateProperty, DueDateProperty } });
      BusinessRules.AddRule(new StartDateGTEndDate { PrimaryProperty = DueDateProperty, AffectedProperties = { StartDateProperty, EndedProperty } });
      BusinessRules.AddRule(new PriorityEnumCheck { PrimaryProperty = PriorityProperty });
      BusinessRules.AddRule(new CheckMarksDueDates { PrimaryProperty = DueDateProperty });
      BusinessRules.AddRule(new UpdateCanChangeResponsiblePerson { PrimaryProperty = ResponsiblePersonProperty });
      BusinessRules.AddRule(new UpdateCanSetComplete { PrimaryProperty = ProgressProperty });
      BusinessRules.AddRule(new UpdateCanSetComplete { PrimaryProperty = ResponsiblePersonProperty });
      BusinessRules.AddRule(new UpdateCanCompleteMarks { PrimaryProperty = ResponsiblePersonProperty });
    }

    private class UpdateCanCompleteMarks : BusinessRule
    {
      protected override void Execute(RuleContext context)
      {
        var target = (ActivityBO)context.Target;
        foreach (var mark in target.Marks)
        {
          mark.UpdateCanComplete();
        }
      }
    }

    private class UpdateCanSetComplete : BusinessRule
    {
      protected override void Execute(RuleContext context)
      {
        var target = (ActivityBO)context.Target;
        target.UpdateCanComplete();
      }
    }

    private class UpdateCanChangeResponsiblePerson : BusinessRule
    {
      protected override void Execute(RuleContext context)
      {
        var target = (ActivityBO)context.Target;
        target.OnPropertyChanged(CanChangeResponsiblePersonProperty);
      }
    }

    private class CheckMarksDueDates : BusinessRule
    {
      protected override void Execute(RuleContext context)
      {
        var target = (ActivityBO)context.Target;
        var dueDate = target.ReadProperty(DueDateProperty);
        foreach (var mark in target.Marks)
          mark.CheckDueDate();
      }
    }

    private class StartDateGTEndDate : BusinessRule
    {
      protected override void Execute(RuleContext context)
      {
        var target = (ActivityBO)context.Target;
        var ended = target.ReadProperty(EndedProperty);
        var dueDate = target.ReadProperty(DueDateProperty);
        var startDate = target.ReadProperty(StartDateProperty);
        if (ended.HasValue && startDate > ended)
          context.AddErrorResult(ValidationErrors.StartDateGreaterThanEndDateError);
        if (dueDate.HasValue && startDate > dueDate)
          context.AddErrorResult(ValidationErrors.StartDateGreaterThanDueDateError);
      }
    }

    private class PriorityEnumCheck : BusinessRule
    {
      protected override void Execute(RuleContext context)
      {
        var target = (ActivityBO)context.Target;
        var priority = target.ReadProperty(PriorityProperty);
        if (!Enum.IsDefined(typeof(ObjectPriority), priority.ToString()))
          context.AddErrorResult(ValidationErrors.PriorityIsNotInRangeError);
      }
    }

    //    Microsoft.Practices.EnterpriseLibrary.Validation.Validator validator = new PropertyValueValidator<ActivityBO>(PriorityProperty.Name,
    //  new EnumConversionValidator(typeof(ObjectPriority)));
    //var validationResults = new ValidationResults();
    //validator.Validate(target, validationResults);
    //if (!validationResults.IsValid)

    #endregion

    #region Authorization rules

    private static void AddObjectAuthorizationRules()
    {
      //BusinessRules.AddRule(typeof(ActivityBO), new IsInRole(AuthorizationActions.CreateObject, "ActivityBO.Create()"));
      //BusinessRules.AddRule(typeof(ActivityBO), new IsInRole(AuthorizationActions.GetObject, "ActivityBO.Get()"));
      //BusinessRules.AddRule(typeof(ActivityBO), new IsInRole(AuthorizationActions.EditObject, "ActivityBO.Edit()"));
      //BusinessRules.AddRule(typeof(ActivityBO), new IsInRole(AuthorizationActions.DeleteObject, "ActivityBO.Delete()"));
    }

    #endregion

    #region Data Access

    private void DataPortal_Create(PersonAndModelCriteria criteria)
    {
      if (criteria.Model == null) throw new ArgumentNullException(ExceptionsMessages.ModelNullReference);
      if (criteria.Person == null) throw new ArgumentNullException(ExceptionsMessages.PersonNullReference);
      using (BypassPropertyChecks)
      {
        ActivityId = Guid.NewGuid();
        ModelId = criteria.Model.ModelId;
        StartDate = DateTime.Now;
        CreatedBy = new PersonInfo(criteria.Person);
        LastChangedBy = new PersonInfo(criteria.Person);
        CreatedOn = DateTime.Now;
        ResponsiblePerson = new PersonInfo(PersonBO.GetEmptyPerson());
      }
      base.DataPortal_Create();
    }

    private void DataPortal_Fetch(SingleCriteria<ActivityBO, Guid> id)
    {
      var currentModel = SecurityChecks.GetCurrentModel();
      var currentUser = SecurityChecks.GetCurrentUser(); // check that user is a participant or creator
      using (var ctx = ContextManager<BusinessControl2DataContext>.GetManager(Database.BizControlDB))
      {
        var data = (from a in ctx.DataContext.Activities
                    where a.ActivityId == id.Value && !a.IsDeleted && a.Model == currentModel.ModelId
                    select a).SingleOrDefault();
        using (BypassPropertyChecks)
        {
          this.LoadData(data);
        }
        // get child data
        if (data != null)
        {
          Comments = ActivityComments.GetActivityComments(data.Comments.OrderBy(c => c.Date).ToArray(), this);
          Participants = ActivityParticipants.GetActivityParticipants(data.Participants.ToArray(), this);
          Marks = ActivityMarks.GetActivityMarks(data.Marks.OrderBy(m => m.DueDate).ThenBy(m => m.CreatedOn).ToArray(), this);
          DependentActivities = ActivityLinks.GetActivityLinks(data.ActivityLinks.ToArray(), this);
        }
      }
    }

    [Transactional(TransactionalTypes.TransactionScope)]
    protected override void DataPortal_Insert()
    {
      using (var ctx = ContextManager<BusinessControl2DataContext>.GetManager(Database.BizControlDB))
      {
        LastChanged = DateTime.Now;
        using (BypassPropertyChecks)
        {
          AddActivity(ctx);
        }
        // update child objects
        Csla.DataPortal.UpdateChild(Comments, this);
        Csla.DataPortal.UpdateChild(Participants, this);
        Csla.DataPortal.UpdateChild(Marks, this);
        Csla.DataPortal.UpdateChild(DependentActivities, this);
        //FieldManager.UpdateChildren(this);
      }
    }

    private void AddActivity(ContextManager<BusinessControl2DataContext> ctx)
    {
      Binary lastTimeStamp = null;
      ctx.DataContext.AddActivity(
        ModelId,
        ActivityId,
        ReadProperty(TagsProperty),
        Description,
        CategoryId,
        ResponsiblePerson == null || ResponsiblePerson.IsEmpty() ? (Guid?)null : ResponsiblePerson.Id,
        Priority,
        Progress,
        Quality,
        IsCompleted,
        StartDate,
        DueDate,
        Ended,
        IsTemplate,
        IsHierarchical,
        IsDeletedField,
        CreatedBy == null || CreatedBy.IsEmpty() ? (Guid?)null : CreatedBy.Id,
        CreatedOn,
        LastChanged,
        LastChangedBy == null || LastChangedBy.IsEmpty() ? (Guid?)null : LastChangedBy.Id,
        ref lastTimeStamp);
      TimeStamp = lastTimeStamp.ToArray();
    }

    private void UpdateActivity(ContextManager<BusinessControl2DataContext> ctx)
    {
      Binary lastTimeStamp = null;
      ctx.DataContext.UpdateActivity(
        ActivityId,
        ReadProperty(TagsProperty),
        Description,
        CategoryId,
        ResponsiblePerson == null || ResponsiblePerson.IsEmpty() ? (Guid?)null : ResponsiblePerson.Id,
        Priority,
        Progress,
        Quality,
        IsCompleted,
        StartDate,
        DueDate,
        Ended,
        IsTemplate,
        IsHierarchical,
        IsDeletedField,
        LastChanged,
        LastChangedBy == null || LastChangedBy.IsEmpty() ? (Guid?)null : LastChangedBy.Id,
        TimeStamp,
        ref lastTimeStamp);
      TimeStamp = lastTimeStamp.ToArray();
    }

    [Transactional(TransactionalTypes.TransactionScope)]
    protected override void DataPortal_Update()
    {
      using (var ctx = ContextManager<BusinessControl2DataContext>.GetManager(Database.BizControlDB))
      {
        if (IsSelfDirty)
        {
          LastChangedBy = new PersonInfo(SecurityChecks.GetCurrentUser());
          LastChanged = DateTime.Now;
          UpdateActivity(ctx);
        }
        // update child objects
        Csla.DataPortal.UpdateChild(Comments, this);
        Csla.DataPortal.UpdateChild(Participants, this);
        Csla.DataPortal.UpdateChild(Marks, this);
        Csla.DataPortal.UpdateChild(DependentActivities, this);
        //FieldManager.UpdateChildren(this);
      }
    }

    [Transactional(TransactionalTypes.TransactionScope)]
    protected override void DataPortal_DeleteSelf()
    {
      DataPortal_Delete(new SingleCriteria<ActivityBO, Guid>(ActivityId));
    }

    [Transactional(TransactionalTypes.TransactionScope)]
    private void DataPortal_Delete(SingleCriteria<ActivityBO, Guid> criteria)
    {
      IsDeletedField = true;
      DataPortal_Update();
    }

    #endregion

    #region Factory Methods

    private ActivityBO()
    { /* require use of factory methods */ }

    public static ActivityBO CopyActivity(ActivityBO sourceActivity)
    {
      if (sourceActivity == null || sourceActivity.IsEmpty()) return null;
      var newActivity = NewActivity(SecurityChecks.GetCurrentUser(), SecurityChecks.GetCurrentModel());
      newActivity.Description = Resources.Strings.PrefixCopy;
      newActivity.DueDate = sourceActivity.DueDate;
      newActivity.CategoryId = sourceActivity.CategoryId;
      newActivity.Priority = sourceActivity.Priority;
      newActivity.ResponsiblePerson = new PersonInfo(sourceActivity.ResponsiblePerson);
      Dictionary<Guid, Guid> participantPairs = new Dictionary<Guid, Guid>();
      newActivity.Participants = ActivityParticipants.CopyParticipants(newActivity, sourceActivity, ref participantPairs);
      newActivity.Marks = ActivityMarks.CopyMarks(newActivity, sourceActivity, participantPairs);
      foreach (var activity in sourceActivity.DependentActivities)
      {
        // PreviousActivity ParentActivity ChildActivities NextActivities HasParentActivity HasPreviousActivity
        // marks.Conditions
      }
      return newActivity;
    }

    public static ActivityBO NewActivity(PersonBO person, ModelBO model)
    {
      return Csla.DataPortal.Create<ActivityBO>(new PersonAndModelCriteria(person, model));
    }

    public static ActivityBO GetActivity(Guid id)
    {
      return Csla.DataPortal.Fetch<ActivityBO>(new SingleCriteria<ActivityBO, Guid>(id));
    }

    public static ActivityBO NewActivity()
    {
      var person = SecurityChecks.GetCurrentUser();
      var model = SecurityChecks.GetCurrentModel();
      return Csla.DataPortal.Create<ActivityBO>(new PersonAndModelCriteria(person, model));
    }

    #endregion


    #region ISupportEmtpyValue Members

    public bool IsEmpty()
    {
      return ActivityId == Guid.Empty;
    }

    #endregion
  }
}
